package org.apache.rocketmq.common;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.annotation.JSONField;
import org.apache.rocketmq.common.attribute.TopicMessageType;
import org.apache.rocketmq.common.constant.PermName;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import static org.apache.rocketmq.common.TopicAttributes.TOPIC_MESSAGE_TYPE_ATTRIBUTE;


/**
 * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
 * @author：陈清风扬，个人微信号：chenqingfengyangjj。
 * @date:2025/1/14
 * @方法描述：封装主题配置信息的对象
 */
public class TopicConfig {

    //分隔符
    private static final String SEPARATOR = " ";

    //默认读队列数量，消费者会从读队列中消费消息
    public static int defaultReadQueueNums = 16;

    //默认写队列数量，生产者会向写队列中发送消息
    public static int defaultWriteQueueNums = 16;

    //Jackson序列化和反序列化主题扩展信息的时候会用上该成员变量
    private static final TypeReference<Map<String, String>> ATTRIBUTES_TYPE_REFERENCE = new TypeReference<Map<String, String>>() {};

    //主题名称
    private String topicName;

    //读队列数量
    private int readQueueNums = defaultReadQueueNums;

    //写队列数量
    private int writeQueueNums = defaultWriteQueueNums;

    //该主题权限，默认是可读可写的
    private int perm = PermName.PERM_READ | PermName.PERM_WRITE;

    //该主题的过滤类型，默认是单个tag标签过滤
    private TopicFilterType topicFilterType = TopicFilterType.SINGLE_TAG;

    //该主题系统标志，这个标志在第六版本代码中还用不到，暂时就是用默认值0
    private int topicSysFlag = 0;

    //该主题是否为顺序消息的主题
    private boolean order = false;

    //这个是主题的扩展信息，可以由用户自己的定义
    //这里我要多说一句，实际上一个主题是否是顺序主题，也就是说假如该主题下的队列只可以顺序发送和消费消息
    //那么这个主题就是一个顺序主题，而这个顺序主题是在创建主题的时候被定义的，具体的信息就定义在下面这个成员变量中
    //在创建顺序主题的时候，会把TopicMessageType.FIFO消息类型存储到这个成员变量中
    private Map<String, String> attributes = new HashMap<>();


    public TopicConfig() {
    }


    public TopicConfig(String topicName) {
        this.topicName = topicName;
    }


    public TopicConfig(String topicName, int readQueueNums, int writeQueueNums) {
        this.topicName = topicName;
        this.readQueueNums = readQueueNums;
        this.writeQueueNums = writeQueueNums;
    }


    public TopicConfig(String topicName, int readQueueNums, int writeQueueNums, int perm) {
        this.topicName = topicName;
        this.readQueueNums = readQueueNums;
        this.writeQueueNums = writeQueueNums;
        this.perm = perm;
    }


    public TopicConfig(String topicName, int readQueueNums, int writeQueueNums, int perm, int topicSysFlag) {
        this.topicName = topicName;
        this.readQueueNums = readQueueNums;
        this.writeQueueNums = writeQueueNums;
        this.perm = perm;
        this.topicSysFlag = topicSysFlag;
    }


    public TopicConfig(TopicConfig other) {
        this.topicName = other.topicName;
        this.readQueueNums = other.readQueueNums;
        this.writeQueueNums = other.writeQueueNums;
        this.perm = other.perm;
        this.topicFilterType = other.topicFilterType;
        this.topicSysFlag = other.topicSysFlag;
        this.order = other.order;
        this.attributes = other.attributes;
    }


    public String encode() {
        StringBuilder sb = new StringBuilder();

        sb.append(this.topicName);
        sb.append(SEPARATOR);


        sb.append(this.readQueueNums);
        sb.append(SEPARATOR);


        sb.append(this.writeQueueNums);
        sb.append(SEPARATOR);


        sb.append(this.perm);
        sb.append(SEPARATOR);


        sb.append(this.topicFilterType);
        sb.append(SEPARATOR);

        if (attributes != null) {
            sb.append(JSON.toJSONString(attributes));
        }

        return sb.toString();
    }


    public boolean decode(final String in) {

        String[] strs = in.split(SEPARATOR);


        if (strs.length >= 5) {
            this.topicName = strs[0];
            this.readQueueNums = Integer.parseInt(strs[1]);
            this.writeQueueNums = Integer.parseInt(strs[2]);
            this.perm = Integer.parseInt(strs[3]);
            this.topicFilterType = TopicFilterType.valueOf(strs[4]);
            if (strs.length >= 6) {
                try {
                    this.attributes = JSON.parseObject(strs[5], ATTRIBUTES_TYPE_REFERENCE.getType());
                } catch (Exception e) {
                }
            }
            return true;
        }
        return false;
    }


    public String getTopicName() {
        return topicName;
    }


    public void setTopicName(String topicName) {
        this.topicName = topicName;
    }

    public int getReadQueueNums() {
        return readQueueNums;
    }

    public void setReadQueueNums(int readQueueNums) {
        this.readQueueNums = readQueueNums;
    }

    public int getWriteQueueNums() {
        return writeQueueNums;
    }

    public void setWriteQueueNums(int writeQueueNums) {
        this.writeQueueNums = writeQueueNums;
    }

    public int getPerm() {
        return perm;
    }

    public void setPerm(int perm) {
        this.perm = perm;
    }

    public TopicFilterType getTopicFilterType() {
        return topicFilterType;
    }

    public void setTopicFilterType(TopicFilterType topicFilterType) {
        this.topicFilterType = topicFilterType;
    }

    public int getTopicSysFlag() {
        return topicSysFlag;
    }

    public void setTopicSysFlag(int topicSysFlag) {
        this.topicSysFlag = topicSysFlag;
    }

    //判断当前主题是否支持顺序消息的方法
    public boolean isOrder() {
        return order;
    }

    public void setOrder(boolean isOrder) {
        this.order = isOrder;
    }

    public Map<String, String> getAttributes() {
        return attributes;
    }

    public void setAttributes(Map<String, String> attributes) {
        this.attributes = attributes;
    }

    /**
     * @课程描述:从零带你写框架系列中的课程，整个系列包含netty，xxl-job，rocketmq，nacos，sofajraft，spring，springboot，disruptor，编译器，虚拟机等等。
     * @author：陈清风扬，个人微信号：chenqingfengyangjj。
     * @date:2025/1/14
     * @方法描述：得到当前主题可以发送和消费的消息类型的方法
     */
    @JSONField(serialize = false, deserialize = false)
    public TopicMessageType getTopicMessageType() {
        //如果属性attributes为空，意味着用户没有为该主题配置消息类型，直接返回普通消息类型即可
        if (attributes == null) {
            return TopicMessageType.NORMAL;
        }
        String content = attributes.get(TOPIC_MESSAGE_TYPE_ATTRIBUTE.getName());
        if (content == null) {
            return TopicMessageType.NORMAL;
        }
        return TopicMessageType.valueOf(content);
    }

    //设置当前主题可以发送和消费的消息类型的方法
    @JSONField(serialize = false, deserialize = false)
    public void setTopicMessageType(TopicMessageType topicMessageType) {
        attributes.put(TOPIC_MESSAGE_TYPE_ATTRIBUTE.getName(), topicMessageType.getValue());
    }


    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }

        TopicConfig that = (TopicConfig) o;

        if (readQueueNums != that.readQueueNums) {
            return false;
        }
        if (writeQueueNums != that.writeQueueNums) {
            return false;
        }
        if (perm != that.perm) {
            return false;
        }
        if (topicSysFlag != that.topicSysFlag) {
            return false;
        }
        if (order != that.order) {
            return false;
        }
        if (!Objects.equals(topicName, that.topicName)) {
            return false;
        }
        if (topicFilterType != that.topicFilterType) {
            return false;
        }
        return Objects.equals(attributes, that.attributes);
    }

    @Override
    public int hashCode() {
        int result = topicName != null ? topicName.hashCode() : 0;
        result = 31 * result + readQueueNums;
        result = 31 * result + writeQueueNums;
        result = 31 * result + perm;
        result = 31 * result + (topicFilterType != null ? topicFilterType.hashCode() : 0);
        result = 31 * result + topicSysFlag;
        result = 31 * result + (order ? 1 : 0);
        result = 31 * result + (attributes != null ? attributes.hashCode() : 0);
        return result;
    }

    @Override
    public String toString() {
        return "TopicConfig [topicName=" + topicName + ", readQueueNums=" + readQueueNums
                + ", writeQueueNums=" + writeQueueNums + ", perm=" + PermName.perm2String(perm)
                + ", topicFilterType=" + topicFilterType + ", topicSysFlag=" + topicSysFlag + ", order=" + order
                + ", attributes=" + attributes + "]";
    }
}
